home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / LargeTreeModelNode.java < prev    next >
Text File  |  1998-06-30  |  16KB  |  520 lines

  1. /*
  2.  * @(#)LargeTreeModelNode.java    1.7 98/02/02
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20.  
  21. package com.sun.java.swing.plaf.basic;
  22.  
  23. import com.sun.java.swing.tree.DefaultMutableTreeNode;
  24. import com.sun.java.swing.tree.TreePath;
  25. import com.sun.java.swing.tree.TreeModel;
  26. import com.sun.java.swing.tree.TreeSelectionModel;
  27.  
  28. /**
  29.  * LargeTreeModelNode is used by AbstractTreeUI to track what has been
  30.  * expanded. LargeTreeModelNode differs from VisibleTreeNode in that it
  31.  * is highly model intensive. That is almost all queries to a
  32.  * LargeTreeModelNode result in the TreeModel being queried. It also
  33.  * will not support odd sized row heights.
  34.  * <p>
  35.  * Warning: serialized objects of this class will not be compatible with
  36.  * future swing releases.  The current serialization support is appropriate
  37.  * for short term storage or RMI between Swing1.0 applications.  It will
  38.  * not be possible to load serialized Swing1.0 objects with future releases
  39.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  40.  * baseline for the serialized form of Swing objects.
  41.  *
  42.  * @version 1.7 02/02/98
  43.  * @author Scott Violet
  44.  */
  45. public class LargeTreeModelNode extends DefaultMutableTreeNode {
  46.     /** Tree UI this is created for. */
  47.     protected AbstractTreeUI  treeUI;
  48.  
  49.     /** Is this node expanded? */
  50.     protected boolean         isExpanded;
  51.  
  52.     /** Index of this node from the model. */
  53.     protected int             childIndex;
  54.  
  55.     public LargeTreeModelNode(AbstractTreeUI treeUI, Object userObject,
  56.             int childIndex) {
  57.     super(userObject);
  58.     this.treeUI = treeUI;
  59.     this.childIndex = childIndex;
  60.     }
  61.  
  62.     /**
  63.      * Returns the model for the current tree.
  64.      */
  65.     public TreeModel getModel() {
  66.     return treeUI.getModel();
  67.     }
  68.  
  69.     /**
  70.      * Returns the index of the reciever in the model.
  71.      */
  72.     public int getChildIndex() {
  73.     return childIndex;
  74.     }
  75.  
  76.     /**
  77.      * Returns the child for the passed in model index.
  78.      */
  79.     public LargeTreeModelNode childAtModelIndex(int index) {
  80.     for(int counter = getChildCount() - 1; counter >= 0; counter--)
  81.         if(((LargeTreeModelNode)getChildAt(counter)).childIndex == index)
  82.         return (LargeTreeModelNode)getChildAt(counter);
  83.     return null;
  84.     }
  85.  
  86.     /**
  87.      * Returns true if this node is visible. This is determined by
  88.      * asking all the parents if they are expanded.
  89.      */
  90.     public boolean isVisible() {
  91.     LargeTreeModelNode         parent = (LargeTreeModelNode)getParent();
  92.  
  93.     if(parent == null)
  94.         return true;
  95.     return (parent.isExpanded() && parent.isVisible());
  96.     }
  97.  
  98.     /**
  99.      * Returns the row of the receiver.
  100.      */
  101.     public int getRow() {
  102.     LargeTreeModelNode        parent = (LargeTreeModelNode)getParent();
  103.  
  104.     if(parent == null) {
  105.         if(treeUI.isRootVisible())
  106.         return 0;
  107.         return -1;
  108.     }
  109.     return parent.getCountTo(childIndex);
  110.     }
  111.  
  112.     /**
  113.      * Expands the receiver. If adjustTree is true didAdjustTree and
  114.      * visibleNodesChanged is messaged.
  115.      */
  116.     public void expand(boolean adjustTree) {
  117.     if(!isExpanded) {
  118.         boolean            visible = isVisible();
  119.  
  120.         isExpanded = true;
  121.         if(visible)
  122.         adjustLargeRowCountBy(getTotalChildCount());
  123.         if(adjustTree) {
  124.         didAdjustTree();
  125.         treeUI.visibleNodesChanged();
  126.         }
  127.         treeUI.pathWasExpanded(new TreePath(getUserObjectPath()));
  128.  
  129.         /* Update the selection model, and the selected entries if
  130.            the receivers row and the row after it are selected. */
  131.         if(treeUI != null && visible) {
  132.         int                     cCount;
  133.         int                     row = getRow();
  134.         TreeSelectionModel      selModel = treeUI.getSelectionModel();
  135.  
  136.         if(selModel != null && (cCount = getTotalChildCount()) > 0 &&
  137.             selModel.isRowSelected(row) &&
  138.            selModel.isRowSelected(row + 1)) {
  139.             TreePath[]          paths = new TreePath[cCount];
  140.  
  141.             for(int counter = 0; counter < cCount; counter++)
  142.             paths[counter] = treeUI.getPathForRow(counter + row+1);
  143.             selModel.addSelectionPaths(paths);
  144.         }
  145.         else if(selModel != null)
  146.             selModel.resetRowSelection();
  147.         }
  148.     }
  149.     }
  150.  
  151.     /**
  152.      * Collapses the receiver. If <code>adjustTree</code> is true
  153.      * didAdjustTree and pathWasCollapsed are messaged.
  154.      */
  155.     public void collapse(boolean adjustTree) {
  156.     if(isExpanded) {
  157.         TreePath[]                selPaths;
  158.         TreeSelectionModel        selModel = null;
  159.  
  160.         if(treeUI != null && (selModel = treeUI.getSelectionModel())
  161.                   != null)
  162.         selPaths = selModel.getSelectionPaths();
  163.         else
  164.         selPaths = null;
  165.         if(isVisible())
  166.         adjustLargeRowCountBy(-getTotalChildCount());
  167.         isExpanded = false;
  168.         if(adjustTree) {
  169.         didAdjustTree();
  170.         treeUI.visibleNodesChanged();
  171.         }
  172.         treeUI.pathWasCollapsed(new TreePath(getUserObjectPath()));
  173.  
  174.         /* update the selection */
  175.         if(selPaths != null) {
  176.         boolean            shouldRemove = false;
  177.         TreePath           ourPath = new TreePath(getUserObjectPath());
  178.  
  179.         for(int counter = selPaths.length - 1; counter >= 0;
  180.             counter--) {
  181.             if(selPaths[counter] != null &&
  182.                ourPath.isDescendant(selPaths[counter]) && 
  183.             !ourPath.equals(selPaths[counter]))
  184.             shouldRemove = true;
  185.             else
  186.             selPaths[counter] = null;
  187.         }
  188.         if(shouldRemove)
  189.             selModel.removeSelectionPaths(selPaths);
  190.         }
  191.     }
  192.     }
  193.  
  194.     /**
  195.      * Returns the number of children in the receiver by descending all
  196.      * expanded nodes and messaging them with getTotalChildCount.
  197.      */
  198.     public int getTotalChildCount() {
  199.     if(isExpanded()) {
  200.         int        retCount = getModel().getChildCount(getUserObject());
  201.  
  202.         for(int counter = getChildCount() - 1; counter >= 0;
  203.         counter--) {
  204.         retCount += ((LargeTreeModelNode)getChildAt(counter))
  205.             .getTotalChildCount();
  206.         }
  207.         return retCount;
  208.     }
  209.     return 0;
  210.     }
  211.  
  212.     /** 
  213.      * Returns true if this node is expanded.
  214.      */
  215.     public boolean isExpanded() {
  216.     return isExpanded;
  217.     }
  218.  
  219.     /**
  220.      * Messaged when the child count has changed and this node hasn't
  221.      * yet been expanded, does nothing.
  222.      */
  223.     public void modelChildCountChanged() {
  224.     }
  225.  
  226.     /**
  227.      * The highest visible nodes have a depth of 0. 
  228.      */
  229.     public int getVisibleLevel() { 
  230.     if (treeUI.isRootVisible()) {
  231.         return getLevel();
  232.     } else {
  233.         return getLevel()-1;
  234.     }
  235.     }
  236.  
  237.     /**
  238.      * Makes the receiver collapse if it is currently expanded, otherwise
  239.      * expands the reciever.
  240.      */
  241.     public void toggleExpanded() {
  242.     if (isExpanded()) {
  243.         collapse(true);
  244.     } else {
  245.         expand(true);
  246.     }
  247.     }
  248.  
  249.     /**
  250.      * Adjust the large row count of the AbstractTreeUI the receiver was
  251.      * created with.
  252.      */
  253.     protected void adjustLargeRowCountBy(int changeAmount) {
  254.     treeUI.largeRowCount += changeAmount;
  255.     }
  256.  
  257.     /**
  258.      * Adds newChild to this nodes children at the appropriate location.
  259.      * The location is determined from the childIndex of newChild.
  260.      */
  261.     protected void addLargeTreeModelNode(LargeTreeModelNode newChild) {
  262.     boolean         added = false;
  263.     int             childIndex = newChild.getChildIndex();
  264.  
  265.     for(int counter = 0, maxCounter = getChildCount();
  266.         counter < maxCounter; counter++) {
  267.         if(((LargeTreeModelNode)getChildAt(counter)).getChildIndex() >
  268.            childIndex) {
  269.         added = true;
  270.         insert(newChild, counter);
  271.         counter = maxCounter;
  272.         }
  273.     }
  274.     if(!added)
  275.         add(newChild);
  276.     }
  277.  
  278.     /**
  279.      * Messaged when a child has been removed from the model at the
  280.      * specified index. Will shift down all the childIndexs that
  281.      * are >= index.
  282.      */
  283.     protected void childRemovedAtModelIndex(int index) {
  284.     LargeTreeModelNode                aChild;
  285.  
  286.     for(int counter = 0, maxCounter = getChildCount();
  287.         counter < maxCounter; counter++) {
  288.         aChild = (LargeTreeModelNode)getChildAt(counter);
  289.         if(aChild.childIndex >= index) {
  290.         /* Since matched and children are always sorted by
  291.            index, no need to continue testing with the above. */
  292.         for(; counter < maxCounter; counter++)
  293.             ((LargeTreeModelNode)getChildAt(counter)).childIndex--;
  294.         }
  295.     }
  296.     }
  297.  
  298.     /**
  299.      * Messaged when a child has been inserted at index. For all the
  300.      * children that have a childIndex >= index their index is incremented
  301.      * by one.
  302.      */
  303.     protected void childInsertedAtModelIndex(int index) {
  304.     LargeTreeModelNode                aChild;
  305.  
  306.     for(int counter = 0, maxCounter = getChildCount();
  307.         counter < maxCounter; counter++) {
  308.         aChild = (LargeTreeModelNode)getChildAt(counter);
  309.         if(aChild.childIndex >= index) {
  310.         /* Since matched and children are always sorted by
  311.            index, no need to continue testing with the above. */
  312.         for(; counter < maxCounter; counter++)
  313.             ((LargeTreeModelNode)getChildAt(counter)).childIndex++;
  314.         }
  315.     }
  316.     }
  317.  
  318.     /**
  319.      * Returns the TreePath for the given row. This will return null
  320.      * if the row is greater than the number of expanded nodes.<p>
  321.      * rowCounter is used to count the number of nodes while descending
  322.      * the hierarchy. <p>
  323.      * If eNode is non-null then eNode will be set to either the matching
  324.      * node, or its parent if the last element has not yet been
  325.      * expanded, or is a leaf.<p>
  326.      * isParentNode will be set to true if the parent is set in
  327.      * eNode, otherwise false.<p>
  328.      * The TreePath will be returned in retPath (if it is non-null)
  329.      * Returns the child index of the returned row in childIndex (if
  330.      * non-null).<p>
  331.      * The reason for all these arguments is different methods call
  332.      * this with different requirements and rather than having 4
  333.      * different methods, there is one big one.<p>
  334.      * If no match is found, false is returned, otherwise true.
  335.      */
  336.     protected boolean getPathForRow(int row, int[] rowCounter,
  337.                     TreePath[] retPath,
  338.                     LargeTreeModelNode[] eNode,
  339.                     boolean[] isParentNode,
  340.                     int[] childIndex) {
  341.     if(row == rowCounter[0]) {
  342.         if(childIndex != null)
  343.         childIndex[0] = getChildIndex();
  344.         if(eNode != null) {
  345.         eNode[0] = this;
  346.         isParentNode[0] = false;
  347.         }
  348.         if(retPath != null)
  349.         retPath[0] = new TreePath(getUserObjectPath());
  350.         return true;
  351.     }
  352.     rowCounter[0]++;
  353.     if(isExpanded) {
  354.         LargeTreeModelNode      aNode;
  355.         int               endIndex;
  356.         int               lastChildIndex = 0;
  357.         int               newChildIndex;
  358.         TreeModel         treeModel = getModel();
  359.  
  360.         for(int counter = 0, maxCounter = getChildCount();
  361.         counter < maxCounter; counter++) {
  362.         aNode = (LargeTreeModelNode)getChildAt(counter);
  363.         newChildIndex = aNode.childIndex;
  364.         if((rowCounter[0] + (newChildIndex - lastChildIndex)) >
  365.            row) {
  366.             if(childIndex != null)
  367.             childIndex[0] = row - rowCounter[0] +
  368.                 lastChildIndex;
  369.             if(retPath != null) {
  370.             Object         child;
  371.             Object[]       thisPath = getUserObjectPath();
  372.             int            pLength = thisPath.length;
  373.             Object[]       newPath = new Object[pLength + 1];
  374.  
  375.             child = treeModel.getChild(userObject,
  376.                       (row - rowCounter[0] + lastChildIndex));
  377.             System.arraycopy(thisPath, 0, newPath, 0, pLength);
  378.             newPath[pLength] = child;
  379.             retPath[0] = new TreePath(newPath);
  380.             }
  381.             if(eNode != null) {
  382.             eNode[0] = this;
  383.             isParentNode[0] = true;
  384.             }
  385.             return true;
  386.         }
  387.         rowCounter[0] += (newChildIndex - lastChildIndex);
  388.         lastChildIndex = newChildIndex + 1;
  389.         if(aNode.getPathForRow(row, rowCounter, retPath, eNode,
  390.                        isParentNode, childIndex)) {
  391.             return true;
  392.         }
  393.         }
  394.         newChildIndex = treeModel.getChildCount(userObject) - 1;
  395.         if((newChildIndex - lastChildIndex) >= 0) {
  396.         if((rowCounter[0] + (newChildIndex - lastChildIndex))
  397.            >= row) {
  398.             Object         child;
  399.  
  400.             if(childIndex != null)
  401.             childIndex[0] = row - rowCounter[0] +
  402.                 lastChildIndex;
  403.             if(retPath != null) {
  404.             Object[]       thisPath = getUserObjectPath();
  405.             int            pLength = thisPath.length;
  406.             Object[]       newPath = new Object[pLength + 1];
  407.  
  408.             child = treeModel.getChild(userObject,
  409.                    (row - rowCounter[0] + lastChildIndex));
  410.             System.arraycopy(thisPath, 0, newPath, 0, pLength);
  411.             newPath[pLength] = child;
  412.             retPath[0] = new TreePath(newPath);
  413.             }
  414.             if(eNode != null) {
  415.             eNode[0] = this;
  416.             isParentNode[0] = true;
  417.             }
  418.             return true;
  419.         }
  420.         rowCounter[0] += (newChildIndex - lastChildIndex) + 1;
  421.         }
  422.     }
  423.     return false;
  424.     }
  425.  
  426.     /**
  427.      * Asks all the children of the receiver for their totalChildCount
  428.      * and returns this value (plus stopIndex).
  429.      */
  430.     protected int getCountTo(int stopIndex) {
  431.     LargeTreeModelNode    aChild;
  432.     int                   retCount = stopIndex + 1;
  433.  
  434.     for(int counter = 0, maxCounter = getChildCount();
  435.         counter < maxCounter; counter++) {
  436.         aChild = (LargeTreeModelNode)getChildAt(counter);
  437.         if(aChild.childIndex >= stopIndex)
  438.         counter = maxCounter;
  439.         else
  440.         retCount += aChild.getTotalChildCount();
  441.     }
  442.     if(parent != null)
  443.         return retCount + ((LargeTreeModelNode)getParent())
  444.         .getCountTo(childIndex);
  445.     if(!treeUI.isRootVisible())
  446.         return (retCount - 1);
  447.     return retCount;
  448.     }
  449.  
  450.     /**
  451.      * Returns, by reference in rowCounter, the row for the given
  452.      * path. This is meant to be called from the root, it will not
  453.      * compute the row of the receiver.<p>
  454.      * Path is the path that is being searched for.
  455.      * pathCounter is the current index into path
  456.      * pathLength is the length of the path (avoids path.length);
  457.      * isInPath is true if the parents path is contained in path.
  458.      * returns true if the row is found.
  459.      */
  460.     protected boolean getRow(Object[] path, int pathCounter,
  461.                  int pathLength, boolean isInPath,
  462.                  int[] rowCounter) {
  463.     isInPath = (isInPath && path[pathCounter].equals(userObject));
  464.     if(isInPath) {
  465.         if(++pathCounter == pathLength)
  466.         return true;
  467.     }
  468.     rowCounter[0]++;
  469.     if(isExpanded) {
  470.         LargeTreeModelNode   aNode;
  471.         int                  endIndex;
  472.         int                  lastChildIndex;
  473.         int                  newChildIndex;
  474.         int                  newRowCount;
  475.         TreeModel            treeModel = getModel();
  476.  
  477.         if(isInPath && (pathCounter + 1) == pathLength)
  478.         endIndex = treeModel.getIndexOfChild(userObject,
  479.                              path[pathLength - 1]);
  480.         else
  481.         endIndex = Integer.MAX_VALUE;
  482.         lastChildIndex = 0;
  483.         newRowCount = rowCounter[0];
  484.         for(int counter = 0, maxCounter = getChildCount();
  485.         counter < maxCounter; counter++) {
  486.         aNode = (LargeTreeModelNode)getChildAt(counter);
  487.         newChildIndex = aNode.childIndex;
  488.         if(newChildIndex >= endIndex) {
  489.             rowCounter[0] = newRowCount +
  490.             (endIndex - lastChildIndex);
  491.             return true;
  492.         }
  493.         newRowCount += (newChildIndex - lastChildIndex);
  494.         lastChildIndex = newChildIndex + 1;
  495.         rowCounter[0] = newRowCount;
  496.         if(aNode.getRow(path, pathCounter, pathLength,
  497.                 isInPath, rowCounter))
  498.             return true;
  499.         newRowCount = rowCounter[0];
  500.         }
  501.         newChildIndex = treeModel.getChildCount(userObject) - 1;
  502.         if(newChildIndex >= 0) {
  503.         if(newChildIndex >= endIndex) {
  504.             rowCounter[0] = newRowCount +
  505.             (endIndex - lastChildIndex);
  506.             return true;
  507.         }
  508.         rowCounter[0] += (newChildIndex - lastChildIndex) + 1;
  509.         }
  510.     }
  511.     return false;
  512.     }
  513.  
  514.     /**
  515.      * Messaged when this node either expands or collapses.
  516.      */
  517.     protected void didAdjustTree() {
  518.     }
  519. }
  520.